{# Chat macros: the transcript's one set of bubbles. A live turn and a history replay draw the same markup: ``user_bubble`` for what was asked, ``streaming_bubble`` while the answer arrives (the script fills its body), ``assistant_bubble`` for a stored, settled message (markdown, the tool trail, the footer). Every hook the scripts read is a data attribute or an id here, never framework scope, which is what lets one macro serve both a history render and the swap at the end of a live turn. #} {% from "components/macros/icons.html" import icon %} {% from "components/macros/layout.html" import avatar %} {# The chat's buttons: outlined and quiet, so the conversation stays louder than the controls. ``tone`` primary is teal; ``size`` sm or xs. #} {% macro button_classes(tone="default", size="sm") -%} {% set tones = {"default": "border-aegis-border text-aegis-muted hover:border-aegis-muted hover:text-aegis-text hover:bg-aegis-border/20", "primary": "btn-primary"} %} btn btn-outline {{ {"sm": "btn-sm", "xs": "btn-xs"}[size] }} {{ tones.get(tone, tones["default"]) }} {%- endmacro %} {# The copy button, one definition for both bubbles: ``text`` is what it copies (an answer's markdown source, not its rendered HTML). app.js does the copying; the icon becomes a tick and pops, then settles back, since without it the button looks broken and gets pressed twice. ``extra`` carries the bubble's reveal rule. #} {% macro copy_button(text, extra="") %} {% endmacro %} {# What a voice control is doing, drawn from its data-state (voice.js only sets it): the resting icon (the caller) while idle; recording, a pulsing dot (the mic only, ``record=True``); thinking, a spinner while audio is heard or fetched; speaking, an equalizer while it plays. The control carries ``group relative``; one set of visuals for the mic and every speaker. #} {% macro voice_states(size="w-4 h-4", record=False) %} {{ caller() }} {% if record %} {% endif %} {{ icon("spinner", "hidden group-data-[state=thinking]:block " ~ size ~ " animate-spin", 1.5, 'data-mic-visual="thinking"') }} {% endmacro %} {# Hear an answer: the stored message, said aloud. Same place and weight as the copy button; voice.js plays it, and plays it unasked when the turn began at the microphone. #} {% macro speak_button(url, extra="") %} {% endmacro %} {% macro user_bubble(text, attachments=None, attachment_names=None) %}
Attached: {{ attachment_names|join(", ") }}
{% endif %}